using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Framework.Timer;
using UnityEngine.Events;

/// <summary>
/// 临时对象类
/// </summary>
public class TempItem
{
    public GameObject obj;
    public long saveTime;
}

namespace Framework.ObjectPool
{
    /// <summary>
    /// 对象池管理器
    /// </summary>
    public class ObjectPoolManager : SingletonMono<ObjectPoolManager>
    {
        
        protected override void Init()
        {
            base.Init();
            GameUpdateMgr.Instance.AddUpdater(Instance.UpdateTemporaryDic,
                GameUpdateMgr.UpdaterType.LaterUpdate); //调用updater
        }

        /// <summary>
        /// 对象池父类对象
        /// </summary>
        private readonly Dictionary<string, GameObject> poolParentList = new Dictionary<string, GameObject>();

        /// <summary>
        /// 永久对象池
        /// </summary>
        private readonly Dictionary<string, Queue> permanentDic = new Dictionary<string, Queue>();

        /// <summary>
        /// 临时对象池
        /// </summary>
        private readonly Dictionary<string, Queue> temporaryDic = new Dictionary<string, Queue>();

        public float removeTime = 60; //临时对象池删除时间（S）
        private float timer = 0; //计时器
        readonly float invokingTime = 2f; //方法调用间隔
        private const int poolCount = 100; //默认对象池储存数量


        #region 更新对象池


        /// <summary>
        /// 更新对象池
        /// </summary>
        private void UpdateTemporaryDic()
        {
            timer += Time.deltaTime;
            if (timer >= invokingTime)
            {

                foreach (var t in temporaryDic.Values)
                {
                    if (t.Count > 0)
                    {
                        if (t.Dequeue() is TempItem temp && SelfTime.GetTimeStamp() - temp.saveTime > removeTime)
                        {
                            Destroy(temp.obj);
                            break;
                        }
                    }
                }

                #region 判断是否有超出限制对象池储存数量

                foreach (var type in permanentDic.Keys)
                {
                    CheckCountInPool(type, false);
                }

                foreach (var type in temporaryDic.Keys)
                {
                    CheckCountInPool(type, true);
                }

                #endregion

                timer = 0;
            }
        }

        /// <summary>
        /// 某一类型对象如果数量过多，则需要移动到回收站
        /// </summary>
        /// <param name="objType">类型</param>
        /// <param name="isTemp">是否是临时对象</param>
        private void CheckCountInPool(string objType, bool isTemp)
        {
            if (!isTemp)
            {
                if (!permanentDic.ContainsKey(objType) || permanentDic[objType].Count <= poolCount) return;
                var item = permanentDic[objType].Dequeue();
                Destroy(item as GameObject);
                Debug.LogWarning("对象池永久对象:" + objType.ToString() + "数量过多，已删除");
            }
            else
            {
                if (!temporaryDic.ContainsKey(objType) || temporaryDic[objType].Count <= poolCount) return;
                var item = temporaryDic[objType].Dequeue();
                Destroy((item as TempItem)?.obj);
                Debug.LogWarning("对象池临时对象:" + objType.ToString() + "数量过多，已删除");
            }

        }

        #endregion

        #region 接口方法

        /// <summary>
        ///  预加载对象
        /// </summary>
        /// <param name="objType"></param>
        /// <param name="abPath"></param>
        /// <param name="resName"></param>
        /// <returns></returns>
        public void PreLoadingObject(string objType, string abPath, string resName,UnityAction<bool> callBack=null)
        {
#if UNITY_EDITOR
            // 编辑器模式下使用同步加载资源
            string suffix;
            System.Type type = typeof(GameObject);  // 这GameObject
            if (type == typeof(GameObject))
                suffix = ".prefab";
            else
            {
                suffix = "";
                Debug.LogError("出现了意料之外的类型：" + type.Name);
            }

            var path = "Assets/abRes/Assetbundle/"+abPath+"/" + resName + suffix;
            var obj = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>(path);

            if (obj != null)
            {
                if (!permanentDic.ContainsKey(objType))
                {
                    permanentDic.Add(objType, new Queue());
                }
                permanentDic[objType].Enqueue(obj);
                callBack?.Invoke(true);
            }
            else
            {
                Debug.LogError($"加载资源失败：{path}");
                callBack?.Invoke(false);
            }
#else
            //todo permanentDic提前加载
            if (!permanentDic.ContainsKey(objType))
            {
                //GameObject  obj=  ABManager.Instance.LoadResource<GameObject>(abPath, resName);
                ABManager.Instance.LoadResourceAsync(abPath, resName, (result) =>
                {
                    if (result != null)
                    {
                        // 如果对象类型不存在于 permanentDic 中，则添加新条目
                        if (!permanentDic.ContainsKey(objType))
                        {
                            permanentDic.Add(objType, new Queue());
                        }

                        // 将加载的对象加入队列
                        permanentDic[objType].Enqueue(result as GameObject);
                        // 回调成功
                        callBack?.Invoke(true);
                    }
                    else
                    {
                        Debug.LogError($"异步加载对象失败：{resName} 来自 {abPath}");
                        // 回调失败
                        callBack?.Invoke(false);
                    }
                });
                
                
            }
            else
            {
                // 对象类型已存在于 permanentDic 中，直接回调成功
                callBack?.Invoke(true);
            }
#endif
        }

        /// <summary>
        /// 从对象池中获取对象（默认从永久池中取出）
        /// </summary>
        /// <param name="objType">对象对应的类型</param>
        /// <param name="abPath">ab包名</param>
        /// <param name="resName">资源名称</param>
        /// <param name="isPoolExist">对象池中是否存在</param>
        /// <param name="isTemp">是否为临时变量</param>
        public GameObject GetObject(string objType, string abPath, string resName, out bool isPoolExist,
            bool isTemp = false)
        {
            object obj = null; //数据对象
            GameObject gameObj = null; //游戏对象
            isPoolExist = true;
            //从永久对象池中获得的对象
            if (!isTemp)
            {
                if (permanentDic.ContainsKey(objType))
                {
                    //拿走该对象
                    if (permanentDic[objType].Count > 0)
                    {
                        //Debug.Log("拿走永久对象池中的对象");
                        obj = permanentDic[objType].Dequeue();
                    }

                    //若对象池里没有该对象
                    else
                    {
                        isPoolExist = false;
                        obj = FindObject(abPath, resName);
                    }

                }
                //若没有该类型
                else
                {
                    isPoolExist = false;
                    obj = FindObject(abPath, resName);
                }
            }
            else
            {
                if (temporaryDic.ContainsKey(objType))
                {
                    //拿走该对象
                    if (temporaryDic[objType].Count > 0)
                    {
                        //Debug.Log("拿走临时对象池中的对象");
                        if (temporaryDic[objType].Dequeue() is TempItem tempObj) obj = tempObj.obj;
                    }

                    //若对象池里没有该对象
                    else
                    {
                        isPoolExist = false;
                        obj = FindObject(abPath, resName);
                    }
                }
                //若没有该类型
                else
                {
                    isPoolExist = false;
                    obj = FindObject(abPath, resName);
                }
            }

            gameObj = obj as GameObject;
            //再次核实是否转化成功
            if (gameObj == null)
            {
                Debug.LogError("为空");
                isPoolExist = false;
                gameObj = FindObject(abPath, resName) as GameObject;
            }

            //将该对象显示
            gameObj.SetActive(true);
            return gameObj;
        }

        /// <summary>
        /// 从对象池中获取未启用的对象（默认从永久池中取出）
        /// </summary>
        /// <param name="objType">对象对应的类型</param>
        /// <param name="abName">ab包名</param>
        /// <param name="resName">资源名称</param>
        /// <param name="isPoolExist"></param>
        /// <param name="isTemp">是否为临时变量</param>
        public GameObject GetObjectForHidden(string objType, string abName, string resName, out bool isPoolExist,
            bool isTemp = false)
        {
            object obj = null; //数据对象
            GameObject gameObj = null; //游戏对象
            isPoolExist = true;
            //从永久对象池中获得的对象
            if (!isTemp)
            {
                if (permanentDic.ContainsKey(objType))
                {
                    //拿走该对象
                    if (permanentDic[objType].Count > 0)
                    {
                        Debug.Log("拿走永久对象池中的对象");
                        obj = permanentDic[objType].Dequeue();
                    }

                    //若对象池里没有该对象
                    else
                    {
                        isPoolExist = false;
                        obj = FindObject(abName, resName);
                    }
                }
                //若没有该类型
                else
                {
                    isPoolExist = false;
                    obj = FindObject(abName, resName);
                }
            }
            else
            {
                if (temporaryDic.ContainsKey(objType))
                {
                    //拿走该对象
                    if (temporaryDic[objType].Count > 0)
                    {
                        //Debug.Log("拿走临时对象池中的对象");
                        TempItem tempObj = temporaryDic[objType].Dequeue() as TempItem;
                        obj = tempObj.obj;
                    }

                    //若对象池里没有该对象
                    else
                    {
                        isPoolExist = false;
                        obj = FindObject(abName, resName);
                    }
                }
                //若没有该类型
                else
                {
                    isPoolExist = false;
                    obj = FindObject(abName, resName);
                }
            }

            gameObj = obj as GameObject;
            //再次核实是否转化成功
            if (gameObj == null)
                gameObj = FindObject(abName, resName) as GameObject;
            //将该对象隐藏
            gameObj.SetActive(false);
            return gameObj;
        }

        /// <summary>
        /// 将对象储存进对象池（默认存入永久池）
        /// </summary>
        /// <param name="objType">对象对应的类型</param>
        /// <param name="obj">对象</param>
        /// <param name="isTemp">是否为临时变量</param>
        public void SaveObject(string objType, GameObject obj, bool isTemp = false)
        {

            //存入永久对象池
            if (!isTemp)
            {
                //如果对象池中没有该类型则创建
                if (!permanentDic.ContainsKey(objType))
                    permanentDic.Add(objType, new Queue());
                //如果对象为空
                if (obj == null)
                    Debug.LogError("您对象池中存入了一个空的对象!");
                //对象返回池子
                obj.SetActive(false);
                //放入父对象
                SortGameObjectFromName(obj);
                //防止点快了
                bool canAddToPool = true;
                foreach (GameObject existObj in permanentDic[objType])
                {
                    if (existObj.GetInstanceID() == obj.GetInstanceID())
                    {
                        canAddToPool = false;
                        break;
                    }
                }

                //将对象添加至对象池
                if (canAddToPool)
                    permanentDic[objType].Enqueue(obj);

            }
            else
            {
                if (!temporaryDic.ContainsKey(objType))
                {
                    Debug.Log("临时对象池中没有该队列,添加");
                    temporaryDic.Add(objType, new Queue());
                }

                //如果对象为空
                if (obj == null)
                    Debug.LogError("您对象池中存入了一个空的对象!");
                //对象返回池子
                obj.SetActive(false);
                //放入父对象
                SortGameObjectFromName(obj);
                //防止点快了
                bool canAddToPool = true;
                foreach (TempItem existTemp in temporaryDic[objType])
                {
                    if (existTemp.obj.GetInstanceID() == obj.GetInstanceID())
                    {
                        canAddToPool = false;
                        break;
                    }
                }

                if (canAddToPool)
                {
                    var tempItem = new TempItem
                    {
                        obj = obj,
                        //Debug.Log(Time.time);
                        saveTime = SelfTime.GetTimeStamp() //储存该对象存入对象池的时间
                    };

                    temporaryDic[objType].Enqueue(tempItem);
                }
            }
        }

        #endregion

        /// <summary>
        /// 归类对象
        /// </summary>
        /// <param name="obj"></param>
        private void SortGameObjectFromName(GameObject obj)
        {
            if (poolParentList.ContainsKey(obj.name))
            {
                obj.transform.SetParent(poolParentList[obj.name].transform, false);
            }
            else
            {
                GameObject fatherObj = new GameObject(obj.name);
                fatherObj.transform.SetParent(transform, false);
                poolParentList.Add(obj.name, fatherObj);
                obj.transform.SetParent(poolParentList[obj.name].transform, false);
            }
        }

        /// <summary>
        /// 在ab包种中寻找该对象
        /// </summary>
        /// <param name="apPath">ab包名</param>
        /// /// <param name="resName">资源名称</param>
        /// <returns></returns>
        private GameObject FindObject(string apPath, string resName)
        {
            GameObject obj = ABManager.Instance.LoadResource<GameObject>(apPath, resName);
            if (obj == null)
            {
                Debug.LogError($"找不到名字为{resName}的物体");
                return null;
            }

            return Instantiate(obj);
        }
    }
}
